# -*- coding: utf-8 -*-
# @Time    : 2024/9/27 17:13
# @Author  : Alvin
# @File    : SyncOrderUpdateImpl.py
# @Des     : 同步单更新

import json
import logging
import time
import traceback
from concurrent.futures import ThreadPoolExecutor
from datetime import datetime

import requests

from constants import Constants
from handler.BaseLogisticsImpl import BaseLogisticsImpl
from utils import CommonUtil, CheUtil


class SyncOrderUpdateImpl(BaseLogisticsImpl):

    def __init__(self):
        super(SyncOrderUpdateImpl, self).__init__()
        self.max_workers = 9
        self.log_text = "同步单更新任务"
        self.isUpdateAll = False
        self.need_update_list = []
        self.stop_hour = 120  # 物流停滞时间
        self.qujian_jg_time = 8  # 取件间隔时间
        self.exlucde_kyes = ["妈妈驿站", "菜鸟驿站", "驿站", "代收", "暂存", "保管", "取件", "自提", "如有取件码问题"]
        # self.init_logging()

    def start_handle_dy_update_task(self, batch_index, db_data_list):
        try:
            is_login = self.check_user_is_logined()
            if not is_login:
                logging.error("用户登录失败，请检查登录数据是否异常")
                return
            with ThreadPoolExecutor(max_workers=self.max_workers) as pool:
                for item in db_data_list:
                    pool.submit(self.handle_sync_order_update_action, item)
            logging.info(f"第【{batch_index}】数据跑完了，共需更新:{len(self.need_update_list)}条数据")
            self.db_util.update_sync_order_update_info(self.need_update_list)
        except Exception as ex:
            logging.error(f"【start_handle_dy_update_task】异常:{str(ex)}")
            logging.error(f"【start_handle_dy_update_task】异常2:%s", traceback.format_exc())

    def handle_sync_order_update_action(self, dbItem):
        try:
            mailNo = dbItem.get("mailNo", None)
            code, name = self.query_express_code(mailNo)
            dbItem['express'] = name
            if not code:
                logging.error(f"【{mailNo}】未查询到快递编码，请检查快递单号是否正确")
                return
            if code == 'shunfeng' or code == 'SF':
                logging.info(f"顺丰单号暂不支持查询:上家单号->{dbItem['mailNo']}")
                return
            logisticsResp = self.queryLogisticsInfoByKuaiDi100(mailNo, code)
            if not logisticsResp:
                return
            self.handleLogisticsResponseInfo(dbItem, logisticsResp)
        except Exception as ex:
            logging.error(f"handle_sync_order_update_action 异常：{str(ex)}")
            logging.error("traceback.format_exc():\n%s" % traceback.format_exc())

    def handle_logistics_is_stop_action(self, currentState, logistice_update_time, dbItem, logisticsItem):
        """
        处理物流是否停滞事件-->当前非签收状态且不是派送和已送货状态，物流更新时间与当前时间超过120小时
        :param currentState:
        :param logistice_update_time:
        :param dbItem:
        :param logisticsItem:
        :return:
        """
        if currentState not in self.qianShouList and '5' not in currentState:
            is_logistics_stop = CommonUtil.get_sub_time(logistice_update_time, self.stop_hour)
            if is_logistics_stop:  # 商家物流与当前时间间隔超过48小时判定为物流停滞，此时将最新的更新上去即可
                des = logisticsItem.get("context")
                for item in self.exlucde_kyes:
                    if item in des:
                        return
                # todo 物流停滞事件处理
                # custom_logistics_item = {
                #     "time": CommonUtil.getFormatTime(),
                #     "context": "商家发起的快件已退回，期待下次继续为您服务",
                #     "ftime": CommonUtil.getFormatTime(),
                #     "areaCode": "CN441900116000",
                #     "areaName": "江苏省，苏州市",
                #     "status": "在途",
                #     "location": "江苏省，苏州市",
                #     "areaCenter": "114.072521,22.806924",
                #     "areaPinYin": "su zhou shi",
                #     "statusCode": "999"
                # }
                # self.realHandleNeedUpdateLogisticsList(dbItem, custom_logistics_item)

    def handleLogisticsResponseInfo(self, dbItem, logisticsResp):
        mailNo = dbItem['mailNo']
        mailNo_self = dbItem['mailNo_self']
        logisticsList = logisticsResp.get("data", None)
        if not logisticsList:
            logging.info(f"快递100查询信息返回为空：{logisticsResp['message']},上家单号：{mailNo}")
            return
        expressLastUpdateTime = dbItem['expressLastUpdateTime']
        hasPushPickingCode = dbItem.get("hasPushPickingCode")
        newData = logisticsList[0]
        currentState = newData['statusCode']
        logistice_update_time = newData.get("time")
        if currentState in self.lanShouList:  # 揽收状态不做处理，只剩下运输中、派件、和签收
            logging.info(f"揽收状态：上家单号->{mailNo},车氏单号->{mailNo_self}")
            self.handle_longtime_not_update_order(dbItem, newData)
        else:
            if expressLastUpdateTime == logistice_update_time:
                """
                    5: 派送中，永远给中转中
                    501：待取件，结束3包
                """
                content = newData.get("context")
                newData['context'] = CommonUtil.filter_text_by_array(content)
                if currentState == '5' and CommonUtil.is_have_keywords(content):
                    if not hasPushPickingCode:  # 结束跟踪，保存三包
                        self.handle_finish_gz_and_save_sb(dbItem, expressLastUpdateTime, newData)
                elif currentState == '501':
                    if not hasPushPickingCode:  # 结束跟踪，保存三包
                        self.handle_finish_gz_and_save_sb(dbItem, expressLastUpdateTime, newData)
                else:
                    self.handle_longtime_not_update_order(dbItem, newData)
            else:  # 时间不同
                """
                时间不同：
                    1、根据时间获取更新位置   
                        a：如果是-1，表明没有更新过，此时应该查看有没有轨迹已经脱离发出省份的，如果有，则跟着更新，如果一条都没有，则不管
                        b：如果不是-1，则表明已经是跟着上家更新了，继续更新即可
                """
                lastUpdateTimeIndex = self.getLastUpdateTimeIndex(expressLastUpdateTime, logisticsList)
                if lastUpdateTimeIndex == -1:
                    routeInfo = logisticsResp.get("routeInfo", None)
                    if not routeInfo:
                        self.handle_next_step(dbItem, currentState, logisticsResp, lastUpdateTimeIndex)
                    else:
                        fromObj = routeInfo.get("from")
                        if not fromObj:
                            self.handle_next_step(dbItem, currentState, logisticsResp, lastUpdateTimeIndex)
                        else:
                            from_name = fromObj.get("name")
                            start_province = CommonUtil.get_province_name(from_name)
                            if not start_province:
                                logging.info(f"未找到出发省份，节点信息：{routeInfo}")
                                self.handle_next_step(dbItem, currentState, logisticsResp, lastUpdateTimeIndex)
                            else:
                                receiveProvince = dbItem.get("receiveProvince")
                                if start_province in receiveProvince or receiveProvince in start_province:
                                    # 江苏发货
                                    self.handle_next_step(dbItem, currentState, logisticsResp, lastUpdateTimeIndex)
                                else:
                                    newList = reversed(logisticsList)
                                    for index, item in enumerate(newList):
                                        areaName = item.get("areaName", None)
                                        location = item.get("location", None)
                                        statusCode = item.get("statusCode", 0)
                                        logistics_time = item.get("time", 0)
                                        if statusCode in self.yunShuZhong and (areaName is not None or location is not None):
                                            current_province = CommonUtil.get_current_address(areaName, location)
                                            if not current_province:
                                                continue
                                            if receiveProvince in current_province or current_province in receiveProvince:
                                                lastUpdateTimeIndex = self.getLastUpdateTimeIndex(logistics_time, logisticsList)
                                                newLogistics = logisticsList[0:lastUpdateTimeIndex + 1]
                                                self.doHandleNeedUpdateLogisticsList(dbItem, newLogistics, currentState)
                                                return
                                            elif start_province not in current_province and current_province not in start_province:
                                                lastUpdateTimeIndex = self.getLastUpdateTimeIndex(logistics_time, logisticsList)
                                                newLogistics = logisticsList[0:lastUpdateTimeIndex + 1]
                                                self.doHandleNeedUpdateLogisticsList(dbItem, newLogistics, currentState)
                                                return

                                    # 如果走到这里还没有跳出去，说明没找到，没找到的原因可能是没有运出发货省份或已退回
                                    self.handle_longtime_not_update_order(dbItem, newData)
                else:
                    self.handle_next_step(dbItem, currentState, logisticsResp, lastUpdateTimeIndex)

    def handle_finish_gz_and_save_sb(self, dbItem, expressLastUpdateTime, newData):
        """
        处理结束跟踪和保存三包
        :param dbItem:
        :param expressLastUpdateTime:
        :param newData:
        :return:
        """
        mailNo = dbItem.get("mailNo")
        mailNo_self = dbItem.get("mailNo_self")
        picking_code = dbItem.get("pickingCode")
        statusCode = newData['statusCode']
        if not picking_code:
            is_qujian_timeout = CommonUtil.get_sub_time(expressLastUpdateTime, self.qujian_jg_time)
            if is_qujian_timeout or statusCode in self.qianShouList:
                express_name = CheUtil.get_express_name(dbItem.get("express"))
                dbItem['pickingCode'] = f'取件请报{express_name}后五位：{mailNo[-5:]}'
            else:
                return

        # 结束跟踪
        dbItem['hasPushPickingCode'] = True
        dbItem['statusCode'] = self.TYPE_SB_PS
        dbItem['statusStr'] = self.TYPE_SB_PS
        self.finish_and_save_sb(mailNo_self, dbItem, newData)

    def handle_longtime_not_update_order(self, dbItem, newData):
        """
        处理长时间未更新的订单，未更新的原因可能存在：1、还没有跑出发货省份；
                                            2、商品已退回；
                                            3、上家物流停滞
                                            排除2、3 之后才加上自定义轨迹，且城市还是给发货地城市，此处从宇物流选择广州，惊云物流选择苏州
        :param dbItem:
        :param newData:
        :return:
        """
        expressLast_pdate_time = dbItem.get("expressLastUpdateTime")
        logistice_des = newData.get("context")
        if "退回" in logistice_des:  # 退回件，直接更新
            self.realHandleNeedUpdateLogisticsList(dbItem, newData)
        else:
            is_logistics_stop = CommonUtil.get_sub_time(expressLast_pdate_time, self.stop_hour)
            if is_logistics_stop:
                currentState = newData.get("statusCode")
                logistice_update_time = newData.get("time")
                self.handle_logistics_is_stop_action(currentState, logistice_update_time, dbItem, newData)
            else:
                is_long_time = CommonUtil.get_sub_time(expressLast_pdate_time, 23)
                if is_long_time:
                    custom_logistics_item = {
                        "time": CommonUtil.getFormatTime(),
                        "context": "您的快件正在全力运输中，由于运输距离较远，物流更新可能不及时，请耐心等待！",
                        "ftime": CommonUtil.getFormatTime(),
                        "areaCode": "CN441900116000",
                        "areaName": "广东省，广州市",
                        "status": "在途",
                        "location": "广东省，广州市",
                        "areaCenter": "114.072521,22.806924",
                        "areaPinYin": "guang zhou shi",
                        "statusCode": "0"
                    }
                    self.realHandleNeedUpdateLogisticsList(dbItem, custom_logistics_item)

    def handle_next_step(self, dbItem, currentState, logisticsResp, lastUpdateTimeIndex):
        """
        根据当前状态处理下一步：
            1、派送中 --> 跟踪结束
            2、中转中 --> 更新物流
        :param dbItem:
        :param currentState:
        :param logisticsResp:
        :param lastUpdateTimeIndex:
        :return:
        """
        logisticsList = logisticsResp.get("data", None)
        if lastUpdateTimeIndex == -1:  # 未从快递100查出来的物流轨迹中找到上次更新的时间，此时除去揽收记录其它轨迹全量更新
            self.doHandleNeedUpdateLogisticsList(dbItem, logisticsList, currentState)
        else:
            newLogistics = logisticsList[0:lastUpdateTimeIndex]
            self.doHandleNeedUpdateLogisticsList(dbItem, newLogistics, currentState)

    def doHandleNeedUpdateLogisticsList(self, dbItem, logisticsList, currentState):
        """
            处理需要更新的物流轨迹列表
            @param:dbItem 数据库参数
            @param:logisticsList 需要更新的物流轨迹列表
        """
        if not logisticsList:
            return
        item = None
        for newItem in reversed(logisticsList):
            statusCode = newItem['statusCode']
            if statusCode in self.lanShouList:  # 揽收记录不做处理
                continue
            else:
                item = newItem
                break
        if not item:
            return
        content = item.get("context")
        item['context'] = CommonUtil.filter_text_by_array(content)
        self.realHandleNeedUpdateLogisticsList(dbItem, item, logisticsList)

    def realHandleNeedUpdateLogisticsList(self, dbItem, logisticsItem, logisticsList=None):
        """
        实际处理需要更新的物流信息列表
        :param dbItem: 原始数据库参数
        :param logisticsItem: 需要更新的快递item
        :param logisticsList: []
        :return:
        """
        if logisticsList is None:
            logisticsList = []
        mailNo_self = dbItem['mailNo_self']
        pickingCode = dbItem['pickingCode']
        statusCode = logisticsItem['statusCode']
        if statusCode in self.lanShouList:  # 揽收记录不做处理
            return
        if statusCode == '501':
            if not pickingCode:
                dbItem['statusCode'] = self.TYPE_ZHONGZHUANZHONG
                dbItem['statusStr'] = self.TYPE_ZHONGZHUANZHONG
                self.handleUploadLogisticsAction(mailNo_self, self.TYPE_ZHONGZHUANZHONG, dbItem, logisticsItem)
            else:
                expressLastUpdateTime = dbItem['expressLastUpdateTime']
                self.handle_finish_gz_and_save_sb(dbItem, expressLastUpdateTime, logisticsItem)
        elif statusCode in self.qianShouList:
            expressLastUpdateTime = dbItem['expressLastUpdateTime']
            if logisticsList and len(logisticsList) > 0:
                for item in logisticsList:
                    i_statusCode = item.get("statusCode")
                    if '5' in i_statusCode:
                        self.handle_finish_gz_and_save_sb(dbItem, expressLastUpdateTime, logisticsItem)
                        break
        else:
            dbItem['statusCode'] = self.TYPE_ZHONGZHUANZHONG
            dbItem['statusStr'] = self.TYPE_ZHONGZHUANZHONG
            self.handleUploadLogisticsAction(mailNo_self, self.TYPE_ZHONGZHUANZHONG, dbItem, logisticsItem)

    def handleUploadLogisticsAction(self, mailNo_self, updateType, dbItem, logisticsItem):
        """
        处理上传物流信息事件，当前任务只处理中转中的任务，当物流到达派送节点时将节点更新为已到达
        :param mailNo_self:
        :param updateType:
        :param dbItem:
        :param logisticsItem:
        :return:
        """
        try:
            logisticsTime = logisticsItem.get("time", None)
            context = logisticsItem.get("context", None)
            description = CheUtil.getLogisticsDescriptionByLogisticsItem(dbItem, updateType, context)
            resp_json = self.post_update_info_2_lq(mailNo_self, description)
            update_status = resp_json.get("success")
            errmsg = resp_json.get("errmsg")
            if not update_status and "将截断字符串或二进制数据" not in errmsg:
                logging.error(f"{self.log_text}[{mailNo_self}]物流更新异常 error:{resp_json}")
                return
            needUpdateParam = {
                "statusCode": updateType,
                "statusStr": updateType,
                "expressLastUpdateTime": logisticsTime,
                "logisticsInfo": description,
                "updateTime": datetime.now(),
                "id": dbItem['id'],
                "addTime": int(time.time() * 1000)
            }
            self.need_update_list.append(needUpdateParam)
        except Exception as ex:
            logging.error(f"【{self.log_text}】handleUploadLogisticsAction异常：{str(ex)},单号{mailNo_self}")
            logging.error(f"【{self.log_text}】handleUploadLogisticsAction异常:%s", traceback.format_exc())

    def post_update_info_2_lq(self, mailNo_self, des):
        """
        上传中转中的信息到蓝桥
        :param mailNo_self:
        :param des: 物流描述
        :return:
        """
        order_detail = self.get_order_detail(mailNo_self)
        headers = CheUtil.get_request_header(self.token, self.requestToken)
        url = "https://apit9.lqfast.com/lanqiao-tms-app/web/trackingInTransit/addMessage"
        json_raows_data = CheUtil.get_update_pre_param(order_detail)
        param = {
            "m": "func",
            "n": "addMessage",
            "p": {
                "jsonRowsData": json.dumps(json_raows_data),
                "unitStr": f"{mailNo_self}@",
                "message": des,
                "messageF": "",
                "messageS": "",
                "tellF_Che": False,
                "tellS_Che": False,
                "gzDate": CommonUtil.get_current_format_time(),
                "outarriveddate": "",
                "chkin": False
            },
            "l": "",
            "v": "",
            "u": CheUtil.get_param_u(self.token),
            "r": "k",
            "s": "k",
            "t": CommonUtil.get_current_timetamp()
        }
        data = json.dumps(param, separators=(',', ':'))
        resp_json = requests.post(url, headers=headers, data=data).json()
        logging.info(f"【{mailNo_self}】物流更新结果：{resp_json}")
        return resp_json

    def finish_and_save_sb(self, mail_no_self, dbItem, logistics_item):
        is_finish_success = self.finish_order_genzong(mail_no_self)
        if not is_finish_success:
            return
        is_save_success, description = self.save_sb(mail_no_self, dbItem, logistics_item)
        if not is_save_success:
            return
        logisticsTime = logistics_item.get("time")
        needUpdateParam = {
            "statusCode": "三包派送",
            "statusStr": "三包派送",
            "expressLastUpdateTime": logisticsTime,
            "logisticsInfo": description,
            "updateTime": datetime.now(),
            "id": dbItem['id'],
            "addTime": int(time.time() * 1000)
        }
        self.need_update_list.append(needUpdateParam)

    def save_sb(self, mail_no_self, dbItem, logistics_item):
        order_detail = self.get_order_detail(mail_no_self)
        if not order_detail:
            logging.error(f"【{mail_no_self}】保存三包失败")
            return
        url = "https://apit9.lqfast.com/lanqiao-tms-app/web/sendPack_SB/saveSendOrders_SB"
        gridjsonAll = CheUtil.get_sb_param(order_detail)
        logistics_context = logistics_item.get("context")
        kd_tel = dbItem.get("kdMobile")
        mailNo = dbItem.get("mailNo")
        if not kd_tel:
            kd_tel = Constants.default_tel
        contentstr = f"{order_detail.get('product')}:{order_detail.get('qty')}"
        des = CheUtil.get_paisong_des(dbItem, logistics_context, mailNo)
        data = {
            "m": "func",
            "n": "saveSendOrders_SB",
            "p": {
                "gridjsonAll": json.dumps(gridjsonAll),
                "sumaccsf": 0,
                "unitstr": f"{order_detail.get('unit')}@",
                "accsfstr": "0@",
                "contentstr": f"{contentstr}|@",
                "accpsstr": "0@",
                "accazstr": "0@",
                "hxnostr": "@",
                "pdremarkstr": "@",
                "username": Constants.sj_name,
                "userid": Constants.sj_phone,
                "vehicleno": "",
                "edinoneflag": "202506281751098481917",
                "edpddate": CommonUtil.get_current_format_time(),
                "yjdate": CommonUtil.get_current_format_time(),
                "checkEdit1": False,
                "baddress": "",
                "btel": "1",
                "kdTel": kd_tel,
                "sendContent": des
            },
            "l": "",
            "v": "",
            "u": CheUtil.get_param_u(self.token),
            "r": "k",
            "s": "k",
            "t": CommonUtil.get_current_timetamp()
        }
        headers = CheUtil.get_request_header(self.token, self.requestToken)
        data = json.dumps(data, separators=(',', ':'))
        resp_json = requests.post(url, headers=headers, data=data).json()
        save_status = resp_json.get("success")
        if not save_status:
            logging.error(f"【{mail_no_self}】结束跟踪失败，接口响应：{resp_json}")
            return False, des
        return True, des

    def finish_order_genzong(self, mail_no_self):
        headers = CheUtil.get_request_header(self.token, self.requestToken)
        url = "https://apit9.lqfast.com/lanqiao-tms-app/web/trackingInTransit/trackEnd_cancel"
        data = {
            "m": "func",
            "n": "trackEnd_cancel",
            "p": {
                "unitStr": f"{mail_no_self}@",
                "outtraceisover": 1
            },
            "l": "",
            "v": "",
            "u": CheUtil.get_param_u(self.token),
            "r": "k",
            "s": "k",
            "t": CommonUtil.get_current_timetamp()
        }
        data = json.dumps(data, separators=(',', ':'))
        resp_json = requests.post(url, headers=headers, data=data).json()
        finish_status = resp_json['success']
        if not finish_status:
            logging.error(f"【{mail_no_self}】结束跟踪失败，接口响应：{resp_json}")
            return False
        return True
